﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Linq.Expressions;
using System.Runtime;
using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices;
using System.Runtime.InteropServices;

using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Derbster.Domain.Test
{
    [TestClass]
    public class MatchUnitTest : ContractUnitTest
    {
        private Match _testMatch;
        private CommandManager _commandManager;
        [TestInitialize]
        public void TestSetup()
        {
            _testMatch = TestHelper.CreateTestMatch(true, true, "TestMatch");
            _commandManager = new CommandManager();
            _commandManager.Clear();
        }

        [TestMethod]
        public void TestCreateBlank()
        {
            var newMatch = new Match();
            Assert.IsNotNull(newMatch);
        }

        [TestMethod]
        public void TestCreateMatch()
        {
            Assert.IsNotNull(_testMatch);
            
            Assert.IsFalse(_testMatch.HasStarted);
            Assert.IsFalse(_testMatch.HasFinished);
            Assert.IsFalse(_testMatch.PeriodInProgress);
            Assert.IsFalse(_testMatch.JamInProgress);

            Assert.AreEqual(1,_testMatch.CurrentPeriod);
            Assert.AreEqual(1, _testMatch.CurrentJamNumber);

            Assert.IsNotNull(_testMatch.BoxEvents);
            Assert.IsNotNull(_testMatch.Jams);
            Assert.IsNotNull(_testMatch.Penalties);

            Assert.IsNull(_testMatch.CurrentJam);
            Assert.IsNotNull(_testMatch.FirstTeamRoster);
            Assert.AreEqual(14, _testMatch.FirstTeamRoster.Count());
            Assert.IsNotNull(_testMatch.SecondTeamRoster);
            Assert.AreEqual(14, _testMatch.SecondTeamRoster.Count());
        }

        [TestMethod]
        public void TestCreateBadFlags()
        {
            bool throws = false;
            try
            {
                var newMatch = new Match(
                    false,
                    true,
                    _testMatch.FirstTeamRoster,
                    _testMatch.SecondTeamRoster,
                    "Test Match");
            }
            catch (Exception)
            {
                throws = true;
            }
            Assert.IsTrue(throws);
        }

        [TestMethod]
        public void TestCreateBadName()
        {
            bool throws = false;
            try
            {
                var newMatch = new Match(
                    true,
                    true,
                    _testMatch.FirstTeamRoster,
                    _testMatch.SecondTeamRoster,
                    "    ");
            }
            catch (Exception)
            {
                throws = true;
            }
            Assert.IsTrue(throws);
        }

        [TestMethod]
        public void TestCreateBadJam()
        {
            _testMatch.BeginMatch();
            _testMatch.BeginPeriod();
            _testMatch.BeginJam();
            _testMatch.CurrentJam.FirstTeamLineup.Blockers = new HashSet<Player>();
            _testMatch.CurrentJam.FirstTeamLineup.Blockers.Add(_testMatch.FirstTeamRoster.Players.First());
            _testMatch.CurrentJam.FirstTeamLineup.Pivot = _testMatch.FirstTeamRoster.Players.First();
            _testMatch.EndJam();
            _testMatch.EndPeriod();
            _testMatch.EndMatch();
            Assert.IsFalse(_testMatch.VerifyMatch());
        }

        [TestMethod]
        public void TestCreateBadPenalty()
        {
            _testMatch.BeginMatch();
            _testMatch.BeginPeriod();
            _testMatch.BeginJam();
            var penalty = new Penalty(_testMatch,_testMatch.FirstTeamRoster.First(),"X",1,1,1);
            penalty.Code = "W";
            _testMatch.Penalties.Add(penalty);
            _testMatch.EndJam();
            _testMatch.EndPeriod();
            _testMatch.EndMatch();
            Assert.IsFalse(_testMatch.VerifyMatch());
        }

        [TestMethod]
        public void TestCreateMatchConstructor()
        {
            var newMatch = new Match(true, false, 
                _testMatch.FirstTeamRoster, 
                _testMatch.SecondTeamRoster,
                "Test Match");
            Assert.IsNotNull(newMatch);
            Assert.IsTrue(newMatch.IsRegulation);
            Assert.IsFalse(newMatch.IsSanctioned);
            Assert.AreEqual(_testMatch.FirstTeamRoster, newMatch.FirstTeamRoster);
            Assert.AreEqual(_testMatch.SecondTeamRoster, newMatch.SecondTeamRoster);
            Assert.AreEqual("Test Match", newMatch.Name);
        }

        [TestMethod]
        public void TestCreateMatchConstructorWithWhere()
        {
            var newMatch = new Match(true, false,
                _testMatch.FirstTeamRoster,
                _testMatch.SecondTeamRoster,
                "Test Match",
                "Nowhere");
            Assert.IsNotNull(newMatch);
            Assert.IsTrue(newMatch.IsRegulation);
            Assert.IsFalse(newMatch.IsSanctioned);
            Assert.AreEqual(_testMatch.FirstTeamRoster, newMatch.FirstTeamRoster);
            Assert.AreEqual(_testMatch.SecondTeamRoster, newMatch.SecondTeamRoster);
            Assert.AreEqual("Test Match", newMatch.Name);
            Assert.AreEqual("Nowhere",newMatch.Location);
        }

        [TestMethod]
        public void TestCreateMatchConstructorWithWhereAndWhen()
        {
            var now = DateTime.Now;
            var newMatch = new Match(true, false,
                _testMatch.FirstTeamRoster,
                _testMatch.SecondTeamRoster,
                "Test Match",
                "Nowhere",
                now);
            Assert.IsNotNull(newMatch);
            Assert.IsTrue(newMatch.IsRegulation);
            Assert.IsFalse(newMatch.IsSanctioned);
            Assert.AreEqual(_testMatch.FirstTeamRoster, newMatch.FirstTeamRoster);
            Assert.AreEqual(_testMatch.SecondTeamRoster, newMatch.SecondTeamRoster);
            Assert.AreEqual("Test Match", newMatch.Name);
            Assert.AreEqual("Nowhere", newMatch.Location);
            Assert.AreEqual(now, newMatch.When);
        }

        private void AssertNewLineup(Lineup lineup)
        {
            Assert.IsNotNull(lineup);
            Assert.AreEqual(0, lineup.Count);
            Assert.IsFalse(lineup.StarPassed);
        }

        private void AssertNewScoring(Scoring scoring)
        {
            Assert.IsNotNull(scoring);
            Assert.IsNotNull(scoring.Passes);
            Assert.AreEqual(0, scoring.Passes.Count);
            Assert.IsFalse(scoring.IsLead);
            Assert.IsFalse(scoring.NoPass);
            Assert.IsFalse(scoring.Called);
        }

        [TestMethod]
        public void TestBeginMatch()
        {
            _testMatch.BeginMatch();
            Assert.IsTrue(_testMatch.HasStarted);
            Assert.AreEqual(1, _testMatch.CurrentPeriod);
            Assert.AreEqual(1, _testMatch.CurrentJamNumber);
            Assert.IsNotNull(_testMatch.CurrentJam);

            AssertNewLineup(_testMatch.CurrentJam.FirstTeamLineup);
            AssertNewLineup(_testMatch.CurrentJam.SecondTeamLineup);

            AssertNewScoring(_testMatch.CurrentJam.FirstTeamScoring);
            AssertNewScoring(_testMatch.CurrentJam.SecondTeamScoring);

            _commandManager.Undo();
            Assert.IsFalse(_testMatch.HasStarted);
            Assert.AreEqual(1,_testMatch.CurrentPeriod);
            Assert.AreEqual(1, _testMatch.CurrentJamNumber);
            Assert.AreEqual(0, _testMatch.Jams.Count);
            Assert.IsNull(_testMatch.CurrentJam);
            // ReSharper disable HeuristicUnreachableCode
            _commandManager.Redo();
            Assert.IsTrue(_testMatch.HasStarted);
            Assert.AreEqual(1,_testMatch.CurrentPeriod);
            Assert.AreEqual(1, _testMatch.CurrentJamNumber);
            Assert.AreEqual(1, _testMatch.Jams.Count);
            Assert.IsNotNull(_testMatch.CurrentJam);
            // ReSharper restore HeuristicUnreachableCode  
        }

        [TestMethod]
        public void TestBeginMatchTwice()
        {
            bool throws=false;
            _testMatch.BeginMatch();
            try
            {
                _testMatch.BeginMatch();      
            }
            catch (Exception )
            {
                throws = true;
            }
            Assert.IsTrue(throws);
        }

        [TestMethod]
        public void TestEndMatch()
        {
            _testMatch.BeginMatch();
            _testMatch.EndMatch();
            Assert.IsTrue(_testMatch.HasFinished);
            _commandManager.Undo();
            Assert.IsFalse(_testMatch.HasFinished);
            _commandManager.Redo();
            Assert.IsTrue(_testMatch.HasFinished);
        }

        [TestMethod]
        public void TestEndBeforeBegin()
        {
            bool throws = false;
            try
            {
                _testMatch.EndMatch();
            }
            catch (Exception )
            {
                throws = true;
            }
            Assert.IsTrue(throws);
        }

        [TestMethod]
        public void TestEndMatchTwice()
        {
            bool throws = false;
            _testMatch.BeginMatch();
            _testMatch.EndMatch();
            try
            {
                _testMatch.EndMatch();
            }
            catch (Exception )
            {
                throws = true;
            }
            Assert.IsTrue(throws);
        }

        [TestMethod]
        public void TestStartPeriod()
        {
            _testMatch.BeginMatch();
            _testMatch.BeginPeriod();
            Assert.IsTrue(_testMatch.PeriodInProgress);
            Assert.IsFalse(_testMatch.JamInProgress);
            Assert.AreEqual(1, _testMatch.CurrentPeriod);
            Assert.AreEqual(1, _testMatch.CurrentJamNumber);

            _commandManager.Undo();
            Assert.IsFalse(_testMatch.PeriodInProgress);
            Assert.IsFalse(_testMatch.JamInProgress);
            Assert.AreEqual(1, _testMatch.CurrentPeriod);
            Assert.AreEqual(1, _testMatch.CurrentJamNumber);
            
            _commandManager.Redo();
            Assert.IsTrue(_testMatch.PeriodInProgress);
            Assert.IsFalse(_testMatch.JamInProgress);
            Assert.AreEqual(1, _testMatch.CurrentPeriod);
            Assert.AreEqual(1, _testMatch.CurrentJamNumber);
        }

        [TestMethod]
        public void TestEndPeriod()
        {
            _testMatch.BeginMatch();
            _testMatch.BeginPeriod();
            _testMatch.EndPeriod();
            Assert.IsFalse(_testMatch.PeriodInProgress);
            Assert.IsFalse(_testMatch.JamInProgress);
            Assert.AreEqual(2, _testMatch.CurrentPeriod);
            Assert.AreEqual(1, _testMatch.CurrentJamNumber);

            _commandManager.Undo();
            Assert.IsTrue(_testMatch.PeriodInProgress);
            Assert.IsFalse(_testMatch.JamInProgress);
            Assert.AreEqual(1, _testMatch.CurrentPeriod);
            Assert.AreEqual(1, _testMatch.CurrentJamNumber);

            _commandManager.Redo();
            Assert.IsFalse(_testMatch.PeriodInProgress);
            Assert.IsFalse(_testMatch.JamInProgress);
            Assert.AreEqual(2, _testMatch.CurrentPeriod);
            Assert.AreEqual(1, _testMatch.CurrentJamNumber);
            
            _testMatch.EndMatch();
        }

        [TestMethod]
        public void TestStartJam()
        {
            _testMatch.BeginMatch();
            _testMatch.BeginPeriod();
            _testMatch.BeginJam();
            Assert.IsTrue(_testMatch.PeriodInProgress);
            Assert.IsTrue(_testMatch.JamInProgress);
            Assert.AreEqual(1, _testMatch.CurrentPeriod);
            Assert.AreEqual(1, _testMatch.CurrentJamNumber);
            
            _commandManager.Undo();
            Assert.IsTrue(_testMatch.PeriodInProgress);
            Assert.IsFalse(_testMatch.JamInProgress);
            Assert.AreEqual(1, _testMatch.CurrentPeriod);
            Assert.AreEqual(1, _testMatch.CurrentJamNumber);
            
            _commandManager.Redo();
            Assert.IsTrue(_testMatch.PeriodInProgress);
            Assert.IsTrue(_testMatch.JamInProgress);
            Assert.AreEqual(1, _testMatch.CurrentPeriod);
            Assert.AreEqual(1, _testMatch.CurrentJamNumber);
        }

        [TestMethod]
        public void TestEndJam()
        {
            _testMatch.BeginMatch();
            _testMatch.BeginPeriod();
            _testMatch.BeginJam();
            _testMatch.EndJam();
            Assert.IsTrue(_testMatch.PeriodInProgress);
            Assert.IsFalse(_testMatch.JamInProgress);
            Assert.AreEqual(1, _testMatch.CurrentPeriod);
            Assert.AreEqual(2, _testMatch.CurrentJamNumber);

            _commandManager.Undo();
            Assert.IsTrue(_testMatch.PeriodInProgress);
            Assert.IsTrue(_testMatch.JamInProgress);
            Assert.AreEqual(1, _testMatch.CurrentPeriod);
            Assert.AreEqual(1, _testMatch.CurrentJamNumber);

            _commandManager.Redo();
            Assert.IsTrue(_testMatch.PeriodInProgress);
            Assert.IsFalse(_testMatch.JamInProgress);
            Assert.AreEqual(1, _testMatch.CurrentPeriod);
            Assert.AreEqual(2, _testMatch.CurrentJamNumber);

            _testMatch.EndPeriod();
            _testMatch.EndMatch();
        }
    }
}
